package com.amadeus.spin.devtool.soap.shooter.config;

import java.io.FileNotFoundException;
import java.io.IOException;

import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.traversal.NodeIterator;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import com.amadeus.spin.devtool.soap.shooter.AbortException;
import com.amadeus.spin.devtool.soap.shooter.FatalException;
import com.amadeus.spin.standard.util.Dom4jUtils;
import com.amadeus.spin.standard.util.FileUtils;
import com.amadeus.spin.standard.util.XPathHelper;

/**
 * DOM representation of the soapshooter scenario file used.
 */
public class SoapshooterScenario {

  private static final Logger LOG = Logger.getLogger(SoapshooterScenario.class);

  private Document mdoc = null;

  public SoapshooterScenario(InputSource is) throws FatalException {
    try {
      mdoc = Dom4jUtils.getDocumentFromFile(is);
    }
    catch (ParserConfigurationException e) {
      logScenarioReadError(e);
    }
    catch (IOException e) {
      logScenarioReadError(e);
    }
    catch (SAXException e) {
      logScenarioReadError(e);
    }
  }

  /**
   * Parse config file and build a DOM.
   *
   * @throws FatalException
   *           if cannot initialize settings
   * @throws AbortException
   */
  public SoapshooterScenario(String scenarioFilePath) throws FatalException, AbortException {
    if (StringUtils.isEmpty(scenarioFilePath)) {
      scenarioFilePath = "scenario.xml";
    }
    try {
      initScenario(scenarioFilePath);
    }
    catch (SAXParseException saxExc) {
      throw new AbortException("Difficulties parsing the scenario file:\n" + scenarioFilePath + " " +
          saxExc.getMessage() + "  [line " +
          saxExc.getLineNumber() + ", column " + saxExc.getColumnNumber() + "]");
    }
    catch (FileNotFoundException e) {
      throw new AbortException(scenarioFilePath + " does not exist.");
    }
    catch (ParserConfigurationException e) {
      throw new AbortException(scenarioFilePath + " is not a valid xml file. " + e.getMessage());
    }
    catch (IOException e) {
      throw new AbortException("Difficulties reading the scenario file: " + e.getMessage());
    }
    catch (SAXException e) {
      throw new AbortException("Difficulties parsing the scenario file: " + e.getMessage());
    }
  }

  /**
   * inits the scenario, builds a dom object from the xml file.
   *
   * @param configFilePath
   * @throws AbortException
   * @throws IOException
   * @throws SAXException
   * @throws ParserConfigurationException
   */
  private void initScenario(String configFilePath) throws AbortException, IOException, SAXException,
      ParserConfigurationException {
    InputSource configIs = null;
    ConfigurationParameters.setEncoding();
    String paramsFileName = ConfigurationParameters.getParamsFileName();
    if (paramsFileName != null) {
      configIs = preProcessInputsource(configFilePath, paramsFileName);
      if (configIs == null) {
        throw new AbortException("Could not process if/else for scenario ");
      }
      mdoc = Dom4jUtils.getDocumentFromFile(configIs);
    }
    else {
      mdoc = Dom4jUtils.getDocumentFromFile(configFilePath);
    }
    if (!isValidScenario()) {
      throw new AbortException(ConfigurationParameters.getScenarioFileName() + " is not a valid scenario file.");
    }
  }

  /**
   * Preprocesses the scenario: Applies the IF / ELSE conditions.
   *
   * @param configFilePath
   * @param paramsFileName
   * @return
   */
  public InputSource preProcessInputsource(String configFilePath, String paramsFileName) {
    String scenarioString = FileUtils.applyScenarioXsltPreProcessingToString(configFilePath, paramsFileName);
    ConfigurationParameters.setScenarioProcessed(scenarioString);
    return FileUtils.stringToInputSource(scenarioString);
  }

  /**
   * Makes basic checks on the scenario content
   *
   * @return
   */
  private boolean isValidScenario() {
    return this.getNode("/scenario") != null;
  }

  private void logScenarioReadError(Exception e) throws FatalException {
    LOG.error("Difficulties reading the soap scenario file" + e.getMessage());
    throw new FatalException("Difficulties reading the soap scenario file", e);
  }

  public NodeIterator getNodeIterator(String path) {
    return XPathHelper.selectNodeIterator(mdoc, path);
  }

  /**
   * Retrieve a specific node
   *
   * @param path
   *          XPath expression
   * @return string value of the node or null if not found
   */
  public Node getNode(String path) {
    return XPathHelper.selectNode(mdoc, path);
  }

  public static void addAttributeToNode(Node node, String attributeKey, String attributeValue) {
    Attr outputDirAttr = node.getOwnerDocument().createAttribute(attributeKey);
    outputDirAttr.setNodeValue(attributeValue);
    node.getAttributes().setNamedItem(outputDirAttr);
  }

  /**
   * Get a scenario property based on an xpath
   * 
   * @param xpath
   * @return
   */
  public String getProperty(String xpath) {
    return XPathHelper.getProperty(mdoc, xpath);
  }
}
